avatar

Lachezar Grigorov

Full stack web and mobile developer

Performance comparison PHP vs Node.js

Last days I've read a lot of posts about the performance difference between PHP and Node.js  but most of them was made with PHP 5 and not in real productive environment so I decide to test the performance of PHP 7 + Nginx vs PHP7 + Swoole vs Node.js vs Node.js + Nginx using WRK HTTP benchmark tool.

Configuration

I've runed all tests on 5$ intance of linode.com 

#cpu
description: CPU
product: Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz
vendor: Intel Corp.
physical id: 400
bus info: cpu@0
version: pc-i440fx-2.6
slot: CPU 0
size: 2GHz
capacity: 2GHz
width: 64 bits
capabilities: fpu fpu_exception wp vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp x86-64 constant_tsc arch_perfmon rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat
configuration: cores=1 enabledcores=1 threads=1

#memory
description: System Memory
physical id: 1000
size: 1GiB
capacity: 1GiB


To run the tests I've installed the following packages:

sudo apt-get update 
sudo apt-get install nginx
sudo apt-get install php-fpm
sudo apt-get install php-dev
sudo apt-get install php-mbstring
sudo apt-get install php7.0-intl
sudo apt-get install mysql-server
sudo apt-get install build-essential libssl-dev git -y
sudo apt-get install unzip
sudo apt-get install zip
sudo apt-get install wrk

PHP Swoole installation

git clone https://github.com/swoole/swoole-src.git
cd swoole-src
phpize
./configure
make
make install
sudo echo "extension=swoole.so" > /etc/php/7.0/cli/php.ini

Node.js installation:

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | bash
source ~/.bashrc
nvm install node
sudo ln -s "$NVM_DIR/versions/node/$(nvm version)/bin/node" "/usr/local/bin/node"
sudo ln -s "$NVM_DIR/versions/node/$(nvm version)/bin/npm" "/usr/local/bin/npm"

I've set worker_connections of nginx to 1024 which is optimal for single core processor and  turned off the access_log. 

sudo nano /etc/nginx/nginx.conf

events {
worker_connections 1024;

}
http {
access_log off;

I've activated opcache too:

opcache.enable=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.save_comments=0
opcache.fast_shutdown=1

Tests

I've used single thread to run the tests because the CPU has only one core.

PHP version

root@localhost:~# php -v
PHP 7.0.22-0ubuntu0.16.04.1 (cli) ( NTS )

Node.js version

root@localhost:~# node -v
v8.6.0

 Test 1:  Hello World 

PHP code

echo "Hello World!";

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9000);

$http->on('request', function ($request, $response) {
$response->header('Content-Type', 'text/plain; charset=utf-8');
$response->end('Hello World');
});

echo "Server run on port 9000";

$http->start();

Node.js code

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8000);

Results

Benchmark results: PHP vs Node.js - Hello World

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3000
Running 1m test @ http://localhost:3000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.70ms 445.51us 14.14ms 87.20%
Req/Sec 6.79k 434.42 7.69k 70.17%
405200 requests in 1.00m, 77.65MB read
Requests/sec: 6752.42
Transfer/sec: 1.29MB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9000
Running 1m test @ http://localhost:9000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 0.87ms 46.30us 4.54ms 93.99%
Req/Sec 28.70k 829.61 29.55k 96.17%
1713236 requests in 1.00m, 294.10MB read
Requests/sec: 28553.42
Transfer/sec: 4.90MB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000
Running 1m test @ http://localhost:8000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.52ms 570.30us 21.97ms 96.42%
Req/Sec 10.04k 0.90k 10.94k 92.33%
599193 requests in 1.00m, 89.14MB read
Requests/sec: 9986.45
Transfer/sec: 1.49MB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000
Running 1m test @ http://localhost:7000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 8.07ms 2.19ms 32.86ms 94.68%
Req/Sec 3.15k 332.57 3.66k 62.83%
187866 requests in 1.00m, 33.49MB read
Requests/sec: 3130.38
Transfer/sec: 571.51KB

Test 2: String concatenating

PHP code

$str = '';
for ($i = 0; $i < 1000; $i++) {
$str .= 's';
}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9001);

$http->on('request', function ($request, $response) {
$response->header('Content-Type', 'text/plain; charset=utf-8');
$str = '';
for ($i = 0; $i < 1000; $i++) {
$str .= 's';
}
$response->end();
});

echo "Server run on port 9001";

$http->start();

Node.js code

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
var str = '';
for (var i = 0; i < 1000; i++) {
str += 's';
}
res.end();
}).listen(8000);

Results

Benchmark results: PHP vs Node.js - String concatenating

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3001
Running 1m test @ http://localhost:3001
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.71ms 575.75us 18.54ms 90.33%
Req/Sec 5.34k 317.28 6.11k 77.83%
318642 requests in 1.00m, 55.90MB read
Requests/sec: 5310.43
Transfer/sec: 0.93MB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9001
Running 1m test @ http://localhost:9001
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.22ms 128.30us 4.71ms 97.43%
Req/Sec 11.30k 462.90 11.82k 93.17%
674725 requests in 1.00m, 108.10MB read
Requests/sec: 11245.31
Transfer/sec: 1.80MB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000
Running 1m test @ http://localhost:8000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.60ms 648.88us 22.61ms 97.30%
Req/Sec 9.75k 1.15k 11.85k 71.17%
582307 requests in 1.00m, 77.19MB read
Requests/sec: 9703.66
Transfer/sec: 1.29MB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000
Running 1m test @ http://localhost:7000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 7.64ms 1.33ms 28.32ms 93.47%
Req/Sec 3.29k 341.92 3.79k 67.00%
196658 requests in 1.00m, 31.87MB read
Requests/sec: 3276.66
Transfer/sec: 543.82KB

Test 3:  Numbers addition.

PHP code

$count = 0;
for ($i = 0; $i < 1000; $i++) {
$count++;
}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9002);

$http->on('request', function ($request, $response) {
$response->header('Content-Type', 'text/plain; charset=utf-8');
$count = 0;
for ($i = 0; $i < 1000; $i++) {
$count++;
}

$response->end();
});

echo "Server run on port 9002";

$http->start();

Node.js code

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
var count = 0;
for (var i = 0; i < 1000; i++) {
count++;
}
res.end();
}).listen(8000);

Results

Benchmark results: PHP vs Node.js - Numbers addition

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3002
Running 1m test @ http://localhost:3002
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.73ms 370.34us 14.09ms 92.17%
Req/Sec 6.73k 366.62 7.31k 76.50%
401710 requests in 1.00m, 70.47MB read
Requests/sec: 6695.06
Transfer/sec: 1.17MB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9002
Running 1m test @ http://localhost:9002
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.82ms 96.64us 3.55ms 94.52%
Req/Sec 13.79k 379.26 14.26k 92.17%
822827 requests in 1.00m, 131.83MB read
Requests/sec: 13713.45
Transfer/sec: 2.20MB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000
Running 1m test @ http://localhost:8000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.07ms 516.25us 20.27ms 96.70%
Req/Sec 12.23k 1.54k 13.77k 70.22%
731643 requests in 1.00m, 96.99MB read
Requests/sec: 12173.77
Transfer/sec: 1.61MB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000
Running 1m test @ http://localhost:7000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 6.73ms 790.93us 19.30ms 91.88%
Req/Sec 3.73k 280.68 4.16k 66.67%
222936 requests in 1.00m, 36.13MB read
Requests/sec: 3715.07
Transfer/sec: 616.58KB

Test 4:  Filling an array

PHP code

$array = array();
for ($i = 0; $i < 1000; $i++) {
$array[] = 's';
}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9003);

$http->on('request', function ($request, $response) {
$response->header('Content-Type', 'text/plain; charset=utf-8');
$array = array();
for ($i = 0; $i < 1000; $i++) {
$array[] = 's';
}
$response->end();
});

echo "Server run on port 9003";

$http->start();

Node.js code

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
var array = [];
for (var i = 0; i < 1000; i++) {
array.push('s');
}
res.end();
}).listen(8000);

Results

Benchmark results: PHP vs Node.js - Filling an array

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3003
Running 1m test @ http://localhost:3003
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.17ms 519.84us 13.91ms 90.60%
Req/Sec 6.02k 512.37 6.64k 72.67%
359544 requests in 1.00m, 63.07MB read
Requests/sec: 5992.33
Transfer/sec: 1.05MB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9003
Running 1m test @ http://localhost:9003
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.18ms 295.58us 5.35ms 91.43%
Req/Sec 11.51k 1.08k 12.52k 87.67%
687490 requests in 1.00m, 110.15MB read
Requests/sec: 11457.89
Transfer/sec: 1.84MB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000
Running 1m test @ http://localhost:8000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.37ms 549.59us 22.29ms 95.52%
Req/Sec 10.66k 0.92k 11.60k 90.50%
636168 requests in 1.00m, 84.33MB read
Requests/sec: 10602.33
Transfer/sec: 1.41MB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000
Running 1m test @ http://localhost:7000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 7.65ms 0.95ms 21.62ms 80.96%
Req/Sec 3.28k 293.56 3.86k 80.33%
195918 requests in 1.00m, 31.75MB read
Requests/sec: 3265.04
Transfer/sec: 541.89KB

Test 4:  Filling an associative array

PHP code

$array = array();
for ($i = 0; $i < 1000; $i++) {
$array["s" . $i] = 's';
}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9004);

$http->on('request', function ($request, $response) {
$response->header('Content-Type', 'text/plain; charset=utf-8');
$array = array();
for ($i = 0; $i < 1000; $i++) {
$array["s" . $i] = 's';
}
$response->end();
});

echo "Server run on port 9004";

$http->start();

Node.js code

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
var array = {};
for (var i = 0; i < 1000; i++) {
array['s' + i] = 's';
}
res.end();
}).listen(8000);

Results

Benchmark results: PHP vs Node.js - Filling an associative array

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3004
Running 1m test @ http://localhost:3004
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 6.80ms 0.90ms 23.24ms 93.70%
Req/Sec 3.70k 250.61 4.18k 78.17%
220722 requests in 1.00m, 38.72MB read
Requests/sec: 3678.66
Transfer/sec: 660.83KB

PHP+Swoole

Running 1m test @ http://localhost:9004
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.99ms 0.97ms 13.19ms 90.62%
Req/Sec 5.03k 546.80 5.78k 80.50%
300325 requests in 1.00m, 48.12MB read
Requests/sec: 5005.39
Transfer/sec: 821.20KB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000
Running 1m test @ http://localhost:8000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 15.61ms 1.56ms 39.78ms 93.53%
Req/Sec 1.61k 99.76 1.76k 84.33%
96108 requests in 1.00m, 12.74MB read
Requests/sec: 1601.61
Transfer/sec: 217.41KB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000
Running 1m test @ http://localhost:7000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 20.74ms 1.87ms 69.53ms 94.15%
Req/Sec 1.21k 76.66 1.30k 78.67%
72321 requests in 1.00m, 11.72MB read
Requests/sec: 1205.15
Transfer/sec: 200.02KB

Test 5:  Reading a file

PHP code

for ($i = 0; $i < 100; $i++) {
$fp = fopen("./demo.txt", "r");
$content = fread($fp, filesize("./demo.txt"));
fclose($fp);
}

PHP Swoole code

$http = new swoole_http_server('0.0.0.0', 9005);

$http->on('request', function ($request, $response) {
$response->header('Content-Type', 'text/plain; charset=utf-8');
for ($i = 0; $i < 100; $i++) {
$fp = fopen("./demo.txt", "r");
$content = fread($fp, filesize("./demo.txt"));
fclose($fp);
}
$response->end('');
});

echo "Server run on port 9005";

$http->start();

Node.js code

var http = require('http');
var fs = require('fs');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
var content;
for (var i = 0; i < 100; i++) {
content = fs.readFileSync('./demo.txt');
}
res.end();
}).listen(8000);

Results

Benchmark results: PHP vs Node.js - Reading a file

PHP 7 + Nginx

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:3005
Running 1m test @ http://localhost:3005
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 12.38ms 1.76ms 43.99ms 84.59%
Req/Sec 2.03k 181.19 2.37k 76.33%
121118 requests in 1.00m, 21.25MB read
Requests/sec: 2018.54
Transfer/sec: 362.61KB

PHP7+Swoole

root@localhost:~# wrk -t1 -d1m -c25 http://localhost:9005
Running 1m test @ http://localhost:9005
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 8.96ms 0.99ms 19.12ms 92.47%
Req/Sec 2.80k 180.41 3.04k 84.00%
167400 requests in 1.00m, 26.82MB read
Requests/sec: 2789.98
Transfer/sec: 457.73KB

Node.js

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:8000
Running 1m test @ http://localhost:8000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 11.10ms 1.84ms 42.33ms 83.49%
Req/Sec 2.27k 308.10 2.80k 74.50%
135305 requests in 1.00m, 17.94MB read
Requests/sec: 2254.60
Transfer/sec: 306.04KB

Node.js + Nginx proxy

root@localhost:~# wrk -t1 -c25 -d1m http://localhost:7000
Running 1m test @ http://localhost:7000
1 threads and 25 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 17.63ms 1.24ms 29.01ms 76.75%
Req/Sec 1.42k 88.52 1.74k 77.67%
85080 requests in 1.00m, 13.79MB read
Requests/sec: 1417.80
Transfer/sec: 235.31KB

Conclusion

Node.js is really powerful and very fast server-side environment but behind Nginx proxy does it not so well. PHP 7 + Nginx superior Node.js + Nginx in every test but on other side Node.js superior PHP 7 + Nginx in 4 from 5 tests. Making a conclusion was easy just use both PHP and Nginx combined. PHP is more suitable for the back-end and front-end  part of the application but Node.js is more suitable for the real time communication, API implementation and time intensive operations.